
#include <stdint.h>
#include <string.h>
#include <ICC/Connector.h>
#include <ICC/Slot.h>

#include "exception.h"
#include "utils.h"
#include "asn1.h"
#include "tlv.h"
#include "dispatcher.h"
#include "ipcMessage.h"
#include "command.h"
#include "HIDRF.h"

#define IS_DEBIT_KEY(app)	((app) == 0x01 ? 0x01 : 0x00)

extern slot_t Slot[1];

int IClass_Init(void){
	Connector_Init();
	CharacterModule_Init();
	Slot_Init();
	HID_RF_Init(ASN1_FRAME_PROTOCOL_PICO15693, 0);
	return 0;
}

int IClass_PowerOn(void){
	uint8_t atr[32];
	uint8_t alen;
	Connector_Init();
	CharacterModule_Init();
	Slot_Init();
	HID_RF_Init(ASN1_FRAME_PROTOCOL_PICO15693, 0);
	HID_RF_SetFeild(1);
	return Slot_PowerOn(Slot, atr, &alen);
}

int IClass_PowerOnSE(void){
	uint8_t atr[32];
	uint8_t alen;
	Connector_Init();
	CharacterModule_Init();
	Slot_Init();
	return Slot_PowerOn(Slot, atr, &alen);
}

int IClass_PowerOffSE(void){
	Slot_PowerOff(Slot);
	return 0;
}

int IClass_Activate(uint16_t techMask){
//		return nfc_activate(techMask, NULL);
	return 0;
}

int IClass_Deactivate(void){
//		nfc_deactivate();
	return 0;
}

int IClass_PowerOff(void){
	Slot_PowerOff(Slot);
	HID_RF_SetFeild(0);
	return 0;
}

int IClass_ExtractData(uint8_t * ptr, uint16_t objectLength, uint8_t tag, uint8_t* pdata, uint8_t* dlen){
	int e = 0;
	
	Try {
		uint16_t clen;
		clen = tlv_search(&ptr, &objectLength, tag);
		if(pdata){
			memcpy(pdata, ptr, clen);
		}
		if(dlen){
			*dlen = clen;
		}
	}
	Catch(e){
	
	}
	
	return e;
}

int IClass_GetSEVersion(uint8_t ver[], uint8_t* len){
	uint8_t resp[64];
	uint8_t rlen = 0;
//		int ret = api_getVersionInfo(resp, &rlen);
	
	int ret = SamCommand_GetVersionInfo(resp, &rlen);
	if(ret){
		return ret;
	}
	uint8_t vlen = 0;
	
	ret = IClass_ExtractData(resp, rlen, ASN1_CLASS_CONTEXT|0, ver, len);
	if(ret){
		return ret;
	}
	ret = IClass_ExtractData(resp, rlen, ASN1_CLASS_CONTEXT|1, ver + *len, &vlen);
	if(ret){
		return ret;
	}
	*len += vlen;
	return ret;
}

//	ISO14443A 0x02 MIFARE, DESFire, JCOP
//	PICO15693 0x04 PicoPass chip support for read and write
//	PICO14443B 0x05 PicoPass chip support for read and write


int IClass_Find(uint8_t csn[/*8*/]){
	uint8_t resp[64];
	uint8_t rlen;
	int ret = SamCommand_PerformAnticollision(ASN1_FRAME_PROTOCOL_PICO15693, resp, &rlen);
	if(ret){
		return ret;
	}
	
	return IClass_ExtractData(resp, rlen, ASN1_CLASS_CONTEXT | 1, csn, NULL);
}


int IClass_FindEx(uint8_t csn[/*8*/]){
	uint8_t cmd[] = {
		0xA0, 0x05, //Tag = PAYLOAD_SAM_COMMAND, Len = 5
		0xB7, 0x03, //Tag = SAMCOMMAND_PERFORM_ANTICOLLISION, Len = 3
		0x80, 0x01, 0x04, //Tag = FRAME_PROTOCOL_LIST, Len = 1, Val = PICO15693
	};
	uint8_t resp[64];
	uint8_t rlen;
	int ret = api_Dispatch(cmd, sizeof(cmd), resp, &rlen);
	if(ret){
		return ret;
	}
	
	return IClass_ExtractData(resp, rlen, ASN1_CLASS_CONTEXT | 1, csn, NULL);
}


int IClass_GetEelementContent(uint8_t content[], uint8_t *len){
	uint8_t cmd[] = {
		0xA0, 0x82,
		0x00, 0x07,
		0xA1, 0x82,
		0x00, 0x03,
		0x80, 0x01,
		0x04, 0x00
	};
	uint8_t resp[64];
	uint8_t rlen;
	int ret = api_Dispatch(cmd, sizeof(cmd), resp, &rlen);
	if(ret){
		return ret;
	}
	
	return IClass_ExtractData(resp, rlen, ASN1_TAG_BITSTRING, content, len);
}

int IClass_LoadKey(uint8_t persistent, uint8_t oid[/*2*/], uint8_t key[/*8*/]){
	uint8_t cmd[] = {
		0xA0, 0x16, //Tag = PAYLOAD_SAM_COMMAND, Len = 22
		0xA5, 0x14, //Tag = PROCESS_CARD_API, Len = 20
		0xA5, 0x12, //Tag = LOAD_KEY, Len = 18
		0x80, 0x01, persistent, //Tag = IS_PERSISTENT_KEY, Len = 1, Val = TRUE
		0x81, 0x03, 0x03, oid[0], oid[1], //Tag = KEY_REFERENCE_OID, Len = 3, Val = 030101
		0x82, 0x08, key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7] //Tag = KEY_VALUE, Len = 8, Val = 1122334455667788
	};

	uint8_t rbuf[8];
	uint8_t rlen;
	
	return api_Dispatch(cmd, sizeof(cmd), rbuf, &rlen);
}

int IClass_RollKey(uint8_t app, uint8_t old_oid[/*2*/], uint8_t new_oid[/*2*/]){
	uint8_t cmd[] = {
		0xA0, 0x13, //Tag = PAYLOAD_SAM_COMMAND, Len = 19
		0xA5, 0x11, //Tag = PROCESS_CARD_API, Len = 17
		0xA0, 0x0F, //Tag = CARD_API_PICOPASS, Len = 15
		0xA4, 0x0D, //Tag = PICOPASS_MUTUAL_AUTH, Len = 13
		0x80, 0x01, IS_DEBIT_KEY(app), //Tag = IS_DEBIT_KEY, Len = 1, Val = TRUE
		0x81, 0x03, 0x03, new_oid[0], new_oid[1], //Tag = KEY_REFERENCE_OID, Len = 3, Val = 030101
		0x82, 0x03, 0x03, old_oid[0], old_oid[1], //Tag = PREVIOUS_KEY_REF_OID, Len = 3, Val = 030026
	};
	uint8_t rbuf[8];
	uint8_t rlen;
	
	return api_Dispatch(cmd, sizeof(cmd), rbuf, &rlen);
}

int IClass_Select(uint8_t book, uint8_t page, uint8_t config[/*8*/]){
	uint8_t cmd[] = {
	0xA0, 0x0C, // Tag = PAYLOAD_SAM_COMMAND, Len = 12
	0xA5, 0x0A, //Tag = PROCESS_CARD_API, Len = 10
	0xA0, 0x08, // Tag = CARD_API_PICOPASS, Len = 8
	0xA0, 0x06, //Tag = PICOPASS_SELECT, Len = 6
	0x80, 0x01, book, //0x00, 		//Tag = BOOK_NUMBER, Len = 1, Val = 0
	0x81, 0x01, page   //0x02 		//Tag = PAGE_NUMBER, Len = 1, Val = 2
	};
	uint8_t rlen;
	
	return api_Dispatch(cmd, sizeof(cmd), config, &rlen);
}

int IClass_Auth(uint8_t app, uint8_t oid[/*2*/]){
	uint8_t cmd[] = {
		0xA0, 0x0E, 		//Tag = PAYLOAD_SAM_COMMAND, Len = 14
		0xA5, 0x0C, 		//Tag = PROCESS_CARD_API, Len = 12
		0xA0, 0x0A, 		//Tag = CARD_API_PICOPASS, Len = 10
		0xA4, 0x08, 		//Tag = PICOPASS_MUTUAL_AUTH, Len = 8
		0x80, 0x01, IS_DEBIT_KEY(app), //Tag = IS_DEBIT_KEY, Len = 1, Val = TRUE
		0x81, 0x03, 0x03, oid[0], oid[1], //Tag = KEY_REFERENCE_OID, Len = 3, Val = 030026
	};

	uint8_t rbuf[8];
	uint8_t rlen;
	return api_Dispatch(cmd, sizeof(cmd), rbuf, &rlen);
}

int IClass_Read(uint8_t block, uint8_t buf[/*8*/]){
	uint8_t cmd [] = {
		0xA0, //Tag = SamCommand (0x00) Constructed Type => 0xA0
		0x09, //Len = 9
		0xA5, //Tag = ProcessCardApi (0x05) Constructed Type => 0xA5
		0x07, //Len = 7
		0xA0, //Tag = CardApiPicopass (0x00) Constructed Type => 0xA0
		0x05, // Len = 5
		0xA1, // Tag = PicopassReadBlock (0x01) Constructed Type => 0xA1
		0x03, // Len = 3
		0x80, // Tag = BlockNumber (0x00) Primitive Type => 0x80
		0x01, // Len = 1
		block, //bkn VALUE = Number of block to be read INTEGER
	};
	uint8_t rlen;
	return api_Dispatch(cmd, sizeof(cmd), buf, &rlen);
}

int IClass_Read4(uint8_t block, uint8_t buf[/*32*/]){
	uint8_t cmd[] = {
		0xA0, //Tag = SamCommand (0x00) Constructed Type => 0xA0
		0x09, //Len = 9
		0xA5, //Tag = ProcessCardApi (0x05) Constructed Type => 0xA5
		0x07, //Len = 7
		0xA0, //Tag = CardApiPicopass (0x00) Constructed Type => 0xA0
		0x05, //Len = 5
		0xA2, //Tag = PicopassRead4Block (0x02) Constructed Type => 0xA2
		0x03, //Len = 3
		0x80, //Tag = BlockNumber (0x00) Primitive Type => 0x80
		0x01, //Len = 1
		block, //bkn VALUE = Number of first block to be read INTEGER
	};
	uint8_t rlen;
	return api_Dispatch(cmd, sizeof(cmd), buf, &rlen);
}


int IClass_Write(uint8_t block, uint8_t buf[/*8*/]){
	uint8_t cmd[] = {
		0xA0, //Tag = SamCommand (0x00) Constructed Type => 0xA0
		0x13, //Len = 19
		0xA5, //Tag = ProcessCardApi (0x05) Constructed Type => 0xA5
		0x11, //Len = 17
		0xA0, //Tag = CardApiPicopass (0x00) Constructed Type => 0xA0
		0x0F, //Len = 15
		0xA5, //Tag = PicopassWriteBlock (0x05) Constructed Type => 0xA5
		0x0D, //Len = 13
		0x80, //Tag = BlockNumber (0x00) Primitive Type => 0x80
		0x01, //Len = 1 
		block, //bkn VALUE = Number of block to be written INTEGER
		0x81, //Tag = Data (0x01) Primitive Type => 0x81
		0x08, //Len = 8
		//bkd VALUE = Data to be written to the block (8 bytes) OCTET STRING
		buf[0], buf[1], buf[2], buf[3],
		buf[4], buf[5], buf[6], buf[7],
	};
		
	uint8_t rbuf[8];
	uint8_t rlen;
	return api_Dispatch(cmd, sizeof(cmd), rbuf, &rlen);
}

static int IClass_IsValidParamBook0(uint8_t page, uint8_t app){
	return ((page < 8) && ((app == 1) || (app == 2)));
}

static int IClass_IsValidParamBook1(uint8_t page, uint8_t app){
	return (page == 0) && (app == 1);
}


int IClass_DefaultUserOID(uint8_t book, uint8_t page, uint8_t app, uint8_t oid[2]){
//		KMC0 - Book 0 Page 0 App 2 (Kc) 0x23 0x03 0x00 0x23
//		KMD1 - Book 0 Page 1 App 1 (Kd) 0x24 0x03 0x00 0x24
//		KMC1 - Book 0 Page 1 App 2 (Kc) 0x25 0x03 0x00 0x25
//		KMD2 - Book 0 Page 2 App 1 (Kd) 0x26 0x03 0x00 0x26
//		KMC2 - Book 0 Page 2 App 2 (Kc) 0x27 0x03 0x00 0x27
//		KMD3 - Book 0 Page 3 App 1 (Kd) 0x28 0x03 0x00 0x28
//		KMC3 - Book 0 Page 3 App 2 (Kc) 0x29 0x03 0x00 0x29
//		KMD4 - Book 0 Page 4 App 1 (Kd) 0x2A 0x03 0x00 0x2A
//		KMC4 - Book 0 Page 4 App 2 (Kc) 0x2B 0x03 0x00 0x2B
//		KMD5 - Book 0 Page 5 App 1 (Kd) 0x2C 0x03 0x00 0x2C
//		KMC5 - Book 0 Page 5 App 2 (Kc) 0x2D 0x03 0x00 0x2D
//		KMD6 - Book 0 Page 6 App 1 (Kd) 0x2E 0x03 0x00 0x2E
//		KMC6 - Book 0 Page 6 App 2 (Kc) 0x2F 0x03 0x00 0x2F
//		KMD7 - Book 0 Page 7 App 1 (Kd) 0x30 0x03 0x00 0x30
//		KMC7 - Book 0 Page 7 App 2 (Kc) 0x31 0x03 0x00 0x31
//		KMTD - Transport Picopass App 1 (Kd) 0x32 0x03 0x00 0x32
//		KMTC - Transport Picopass App 2 (Kc) 0x33 0x03 0x00 0x33
//		KMD0B1 - Book 1 Page 0 App 1 (Kd) 0x34 0x03 0x00 0x34

	static const uint8_t OID_Table[8][2] = {
		[0] = {0x22, 0x23},	
		[1] = {0x24, 0x25},	
		[2] = {0x26, 0x27},
		[3] = {0x28, 0x29},
		[4] = {0x2A, 0x2B},
		[5] = {0x2C, 0x2D},
		[6] = {0x2E, 0x2F},
		[7] = {0x30, 0x31},
	};
	
	if(book == 0){
		if(!IClass_IsValidParamBook0(page, app)){
			return -1;
		}
		oid[0] = 0x00;
		oid[1] = OID_Table[page][app - 1];
		return 0;
	}
	if(book == 1){
		if(!IClass_IsValidParamBook1(page, app)){
			return -2;
		}		
		oid[0] = 0x00;
		oid[1] = 0x34;
		return 0;
	}
	return -3;
}




